import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { DragControls } from 'three/addons/controls/DragControls.js';
import Stats from 'three/addons/libs/stats.module.js';
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
import { TransformControls } from 'three/addons/controls/TransformControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'
import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js';
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
import { GLTFExporter } from 'three/addons/exporters/GLTFExporter.js'


export default class Scene3d {

    selectObject;

    constructor(_element) {
        this._initScene(_element);
    }

    /**
     * 初始化场景
     */
    initScene() {
        this.scene = new THREE.Scene();
        //this.scene.fog = new THREE.Fog(0x111111, 150, 200);
        // show axes in the screen
        let axes = new THREE.AxesHelper(5);
        this.scene.add(axes);

        let helper = new THREE.GridHelper(50, 20, 0xCD3700, 0x4A4A4A);//网格线
        this.scene.add(helper);
    }
    /**
     * 初始化相机
     */
    initCamera() {
        this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
        // position and point the camera to the center of the scene
        this.camera.position.set(-30, 40, 30);
        this.camera.lookAt(this.scene.position);
    }

    /**
     * 初始化渲染器
     */
    initRenderer(_element) {

        // create a render and set the size
        this.renderer = new THREE.WebGLRenderer();
        this.renderer.setClearColor(new THREE.Color(0x87CEEB));
        this.renderer.setSize(window.innerWidth - 390, window.innerHeight);
        this.renderer.antialias = true;

        this.renderer.alpha = true;

        this.renderer.precision = 'mediump'
        // add the output of the renderer to the html element
        this.container = document.getElementById(_element);
        this.container.appendChild(this.renderer.domElement);
    }

    /**
     * 性能检测
     */
    initStats() {
        this.stats = new Stats();
        this.stats.domElement.style.position = 'absolute';
        this.stats.domElement.style.left = '200px';
        this.container.appendChild(this.stats.dom);
    }

    initControls() {
        this.controls = new OrbitControls(this.camera, this.renderer.domElement);
        // 设置控制器阻尼，让控制器更有真是效果，必须在动画循环里调用update（）
        //this.controls.enableDamping = true;
        this.controls.minPolarAngle = 0;
        this.controls.maxPolarAngle = 75 / 180 * Math.PI;
    }

    /**
     * 初始化灯光
     */
    initLight() {
        this.light = new THREE.SpotLight(0xffffff);
        this.light.position.set(-300, 600, -400);
        this.light.castShadow = true;
        this.scene.add(this.light);
        this.scene.add(new THREE.AmbientLight(0x404040));
    }

    update() {
        this.controls.update();
        this.stats.update();

    }


    changeMaterial(object) {
        let material = new THREE.MeshLambertMaterial({
            color: 0xffffff * Math.random(),
            transparent: object.material.transparent ? false : true,
            opacity: 0.8
        });
        object.material = material;
    }

    onMouseDblclick(event, _that) {
        //alert("a");
        //获取raycaster和所有模型相交的数组，其中的元素按照距离排序，越近的越靠前
        let intersects = _that.getIntersects(event, _that);
        console.log(intersects);
        // console.log(intersects[0].object);

        //获取选中最近的Mesh对象
        //instance坐标是对象，右边是类，判断对象是不是属于这个类的
        let _name = ''; //&& intersects[0].object.type === 'Mesh'
        if (intersects.length !== 0) {
            //intersects[0].object.material.color.set(0x00FF00);
            //console.log(intersects[0].object.material.color);
            for (let _index = 0; _index < intersects.length; _index++) {
                _name = intersects[_index].object.name;
                if (_name == 'X' || _name == 'Y' || _name == 'Z'
                    || _name == 'XY' || _name == 'XZ' || _name == 'YZ'
                    || _name == 'XYZ' || _name == 'E' || _name == 'XYZE'
                    || _name == 'START' || _name == 'END') {
                    continue
                }
                if (intersects[_index].object.type === 'Mesh') {
                    _that.selectObject = intersects[_index].object;
                    if (_that.selectCallBack) {
                        _that.selectCallBack(_that.selectObject);
                    }
                    break;
                }
            }

            //changeMaterial(selectObject)

        } else {
            console.log('未选中 Mesh!');
        }
    }

    //获取与射线相交的对象数组
    getIntersects(event, _that) {

        console.log('target = ',event.target.tagName );
        
        if(event.clientX < 190){
            return [];
        }

        if(event.clientX > window.innerWidth - 200){
            return [];
        }

        if(event.target.tagName != 'CANVAS'){
            return [];
        }

        event.preventDefault();// 阻止默认的点击事件执行, https://developer.mozilla.org/zh-CN/docs/Web/API/Event/preventDefault
        // console.log("event.clientX:" + event.clientX);
        // console.log("event.clientY:" + event.clientY);

        //声明 rayCaster 和 mouse 变量
        let rayCaster = new THREE.Raycaster();
        let mouse = new THREE.Vector2();

        //通过鼠标点击位置，计算出raycaster所需点的位置，以屏幕为中心点，范围-1到1
        mouse.x = ((event.clientX - _that.container.getBoundingClientRect().left) / _that.container.offsetWidth) * 2 - 1;
        mouse.y = -((event.clientY - _that.container.getBoundingClientRect().top) / _that.container.offsetHeight) * 2 + 1; //这里为什么是-号，没有就无法点中

        //通过鼠标点击的位置(二维坐标)和当前相机的矩阵计算出射线位置
        rayCaster.setFromCamera(mouse, _that.camera);

        //获取与射线相交的对象数组， 其中的元素按照距离排序，越近的越靠前。
        //+true，是对其后代进行查找，这个在这里必须加，因为模型是由很多部分组成的，后代非常多。
        let intersects = rayCaster.intersectObjects(_that.scene.children, true);

        //返回选中的对象
        return intersects;
    }

    // 窗口变动触发的方法
    onWindowResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
    }

    //键盘按下触发的方法
    onKeyDown(event, _that) {
        switch (event.keyCode) {
            case 13:
                this.initCamera();
                this.initControls();
                break;
            case 87: // W
                _that.transformControls.setMode('translate');
                break;
            case 69: // E
                _that.transformControls.setMode('rotate');
                break;
            case 82: // R
                _that.transformControls.setMode('scale');
                break;
        }
    }




    _initScene(_element) {

        this.initScene();
        this.initCamera();
        this.initRenderer(_element);
        this.initStats();
        this.initLight();
        this.initControls();

        this.transformControls = new TransformControls(this.camera, this.renderer.domElement);
        this.scene.add(this.transformControls);


        let _that = this;

        this.transformControls.addEventListener('dragging-changed', function (event) {
            _that.controls.enabled = !event.value;
        });

        addEventListener('click', function (event) {
            _that.onMouseDblclick(event, _that)
        }, false);
        //addEventListener('resize', onWindowResize, false);
        addEventListener('keydown', function (event) {
            _that.onKeyDown(event, _that);
        }, false);
        function animate() {
            // if (selectObject != undefined && selectObject != null) {
            //     //renderDiv(selectObject);
            // }
            requestAnimationFrame(animate);
            _that.renderer.render(_that.scene, _that.camera);
            _that.update(_that);
            //判断渲染器是否调整，若调整，相机也需要调整aspect
            // if (resizeRendererToDisplaySize(renderer)) {
            //     const canvas = renderer.domElement;
            //     //重新设置摄像机看视锥体的横纵比，横纵比一般为屏幕宽高比，不然会挤压变形
            //     camera.aspect = canvas.clientWidth / canvas.clientHeight;
            //     camera.updateProjectionMatrix();
            // }
        }
        animate();
    }

    setSelect3DObject(_callback) {
        this.selectCallBack = _callback;
    }

    setTransformControlModal(_modal) {
        if (!_modal) {
            _modal = 'translate';
        }
        this.transformControls.setMode(_modal);
    }

    controlObject(_object) {
        this.transformControls.attach(_object)
    }


    /**
     * 加载，模型
     * @param {模型信息} _modal 
     */
    add3DObject(_modal) {
        const loader = new OBJLoader();
        let _that = this;
        loader.load(
            // resource URL
            _modal.path,
            // called when resource is loaded
            function (object) {
                // let material = new THREE.MeshBasicMaterial({});
                // //obj模型是有无数个mesh子元素组成，所以我们可以对单个child的mesh进行操作
                // object.children.forEach(function (child) {
                //     child.material = material;
                //     // child.material=new THREE.MeshBasicMaterial({map:new THREE.TextureLoader().load('./statics/imgs/floor.jpg'),side:THREE.DoubleSide});
                //     child.geometry.computeFaceNormals();
                //     child.geometry.computeVertexNormals();
                // });
                object.scale.set(1, 1, 1);
                _that.scene.add(object);
            },
            // called when loading is in progresses
            function (xhr) {
                console.log((xhr.loaded / xhr.total * 100) + '% loaded');
            },
            // called when loading has errors
            function (error) {
                console.log('An error happened', error);
            }
        );
    }

    addGltfObject(_modal) {
        const loader = new GLTFLoader();
        const dracoLoader = new DRACOLoader();
        dracoLoader.setDecoderPath('/js/three/examples/jsm/libs/draco/');
        loader.setDRACOLoader(dracoLoader);
        let _that = this;
        loader.load(
            // resource URL
            _modal.path,
            // called when the resource is loaded
            function (gltf) {

                //需要添加的部分
                gltf.scene.traverse(function (child) {
                    if (child.isMesh) {
                        child.material.emissive = child.material.color;
                        child.material.emissiveMap = child.material.map;
                    }
                });
                _that.scene.add(gltf.scene);
                //_that.scene.add(_that.merge(gltf.scene));

                // gltf.animations; // Array<THREE.AnimationClip>
                // gltf.scene; // THREE.Group
                // gltf.scenes; // Array<THREE.Group>
                // gltf.cameras; // Array<THREE.Camera>
                // gltf.asset; // Object

            },
            // called while loading is progressing
            function (xhr) {
                console.log((xhr.loaded / xhr.total * 100) + '% loaded');
            },
            // called when loading has errors
            function (error) {
                console.log('An error happened', error);
            }
        );
    }
    addJpgBackgroup(_modal) {
        const texture = new THREE.TextureLoader().load(_modal.path);
        texture.mapping = THREE.EquirectangularReflectionMapping;
        texture.magFilter = THREE.LinearFilter;//③
        texture.minFilter = THREE.LinearMipmapLinearFilter;//④
        texture.encoding = THREE.sRGBEncoding;
        //texture.repeat.set( 4, 4 );
        this.scene.background = texture;
        this.scene.environment = texture;
    }
    addHdrBackgroup(_modal) {
        const rgbeLoader = new RGBELoader();
        //资源较大，使用异步加载
        let _that = this;
        rgbeLoader.loadAsync(_modal.path).then((texture) => {
            texture.mapping = THREE.EquirectangularReflectionMapping;
            //将加载的材质texture设置给背景和环境
            _that.scene.background = texture;
            _that.scene.environment = texture;
        });
    }

    merge(object) {
        const geometries = [];
        const materialArr = [];
        object.traverse((obj) => {
            const child = obj;
            if (child.isMesh) {
                const geo = child.geometry.clone();
                if (Array.isArray(child.material)) {
                    child.material = child.material[0];
                }
                materialArr.push(child.material);
                geo.index = null;
                child.updateWorldMatrix(true, true);
                geo.applyMatrix4(child.matrixWorld);
                geometries.push(geo);
            }
        });
        // console.log(geometries);
        const buffergeo = BufferGeometryUtils.mergeBufferGeometries(
            geometries,
            true
        );
        // console.log(buffergeo);
        const mesh = new THREE.Mesh(buffergeo, materialArr);

        return mesh;
    }
    add3DBox(_modal) {
        const geometry = new THREE.BoxGeometry(1, 1, 1);
        const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
        const cube = new THREE.Mesh(geometry, material);
        this.scene.add(cube);
    }

    deleteObject(_object) {
        this.transformControls.detach();
        _object.removeFromParent();
        // if (_object) {
        //     _object.traverse(function (item) {
        //         if (item instanceof THREE.Mesh) {
        //             if (Array.isArray(item.material)) {
        //                 item.material.forEach(a => {
        //                     a.dispose()
        //                 })
        //             } else {
        //                 item.material.dispose() // 删除材质
        //             }
        //             item.geometry.dispose() // 删除几何体
        //         }
        //         item = null
        //     })
        //     // 删除场景对象scene的子对象group
        //     this.scene.remove(_object);
        // }
    }


    getObjectTree() {
        let _children = this.scene.children;
        let _objects = [];
        _children.forEach(_item => {
            if (_item instanceof THREE.AxesHelper) {
                return;
            }
            if (_item instanceof THREE.GridHelper) {
                return;
            }
            if (_item instanceof THREE.SpotLight) {
                return;
            }
            if (_item instanceof THREE.AmbientLight) {
                return;
            }
            if (_item instanceof TransformControls) {
                return;
            }
            _objects.push(_item)
        })
        return _objects;
    }
    getAllObject() {
        let _objects = [];
        let _name = '';
        this.scene.traverse(function (child) {
            if (child instanceof THREE.Mesh) {
                _name = child.name;
                if (_name == 'X' || _name == 'Y' || _name == 'Z'
                    || _name == 'XY' || _name == 'XZ' || _name == 'YZ'
                    || _name == 'XYZ' || _name == 'E' || _name == 'XYZE'
                    || _name == 'START' || _name == 'END') {
                    return;
                }
                _objects.push(child);
            }
        });
        return _objects;
    }

    getObject(_uuid) {
        return this.scene.getObjectById(_uuid);
    }

    exportScene() {
        return JSON.stringify(this.scene.toJSON());
    }

    /**
 * gltf和glb导出器
 * @param option
 */
    exporterGlb() {
        const gltfExporter = new GLTFExporter();
        const options = {
            trs: false, //导出位置、旋转和缩放，而不是每个节点的矩阵 默认是false
            onlyVisible: true, //只导出可见的对象 默认是true
            truncateDrawRange: true,
            binary: true, //binary==true 导出glb | binary==false 导出gltf
            forceindices: false, //为非索引几何图形生成索引并导出它们 默认false
            maxTextureSize: 4096, //限制图像的最大尺寸（宽度、高度）为给定值。默认是无限
        };

        let _children = this.scene.children;
        let _objects = [];
        _children.forEach(_item => {
            if (_item instanceof THREE.AxesHelper) {
                return;
            }
            if (_item instanceof THREE.GridHelper) {
                return;
            }
            if (_item instanceof THREE.SpotLight) {
                return;
            }
            if (_item instanceof THREE.AmbientLight) {
                return;
            }
            if (_item instanceof TransformControls) {
                return;
            }
            _objects.push(_item)
        })

        return new Promise((resolve, reject) => {
            gltfExporter.parse(
                _objects, //这是个数组
                function (result) {
                    let _data = '';
                    //if (result instanceof ArrayBuffer) {
                    _data = new Blob([result], { type: 'application/octet-stream' }, 'hc_scene.glb');
                    // } else {
                    //     const output = JSON.stringify(result, null, 2);
                    //     save(new Blob([output], { type: 'text/plain' }), 'scene.gltf');
                    // }
                    resolve(_data)
                },
                function (error) {
                    console.error(err);
                    reject(error)
                },
                options
            );
        })


    }
}